package me.seu.demo.service.operate;

import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

/**
 * 开工模型
 *
 * @author liangfeihu
 * @since 2022/9/5 11:43
 */
@Slf4j
public class OpModelUtil {

    /**
     * 计算两数差分取绝对值
     */
    private static Integer getDiff(Integer b1, Integer b2) {
        return Math.abs(b2 - b1);
    }

    /**
     * 计算数组差分取绝对值
     */
    private static List<Integer> getDiffArray(List<Integer> list) {
        List<Integer> result = new ArrayList<>();
        for (int i = 0; i < list.size() - 1; i++) {
            Integer diff = getDiff(list.get(i + 1), list.get(i));
            result.add(diff);
        }
        return result;
    }

    /**
     * 最大波动值：记为∆M
     * 获取数组中最大最小值差值
     */
    public static Integer getMaxMinDiff(List<Integer> array) {
        int max = array.get(0);
        int min = array.get(0);
        for (Integer value : array) {
            if (value > max) {
                max = value;
            }
            if (value < min) {
                min = value;
            }
        }
        return max - min;
    }

    /**
     * 获取数组差分占比列表
     */
    public static List<BigDecimal> getDiffPartList(List<Integer> dataList, Integer maxMinDiff) {
        BigDecimal maxDiff = new BigDecimal(maxMinDiff);
        List<Integer> diffList = getDiffArray(dataList);

        List<BigDecimal> result = new ArrayList<>();
        for (int i = 0; i < diffList.size(); i++) {
            try {
                BigDecimal percent = new BigDecimal(diffList.get(i)).divide(maxDiff, 2, RoundingMode.HALF_UP);
                result.add(percent);
            } catch (Exception e) {
                log.error("error index={}", i, e);
            }
        }
        return result;
    }

    /**
     * 获取差分占比中稳定的段
     */
    private static List<Point> getStableSegmentList(List<BigDecimal> list, BigDecimal stop, Integer opNum) {
        List<Point> segmentList = new ArrayList<>();
        int index = 0;
        int firstPoint = 0;
        while (index < list.size()) {
            BigDecimal current = list.get(index);
            if (current.compareTo(stop) < 0) {
                firstPoint = index;

                int j = index + 1;
                while (j < list.size()) {
                    BigDecimal currValue = list.get(j);
                    if (currValue.compareTo(stop) < 0) {
                        j++;
                    } else {
                        break;
                    }
                }

                if (j == list.size()) {
                    if (j - firstPoint >= opNum) {
                        segmentList.add(new Point(firstPoint, j - 1));
                    }
                } else {
                    BigDecimal end = list.get(j - 1);
                    BigDecimal next = list.get(j);
                    if (end.compareTo(stop) < 0 && next.compareTo(stop) >= 0 && j - firstPoint >= opNum) {
                        segmentList.add(new Point(firstPoint, j - 1));
                    }
                }

                index = j;
            }
            index++;
        }
        return segmentList;
    }

    /**
     * 从平均值最小的稳定段中求开工阈值
     */
    private static Integer getOpThreshold(List<Point> segmentList, List<Integer> dataList) {
        List<List<Integer>> opDataList = new ArrayList<>();
        List<Long> averageList = new ArrayList<>();
        for (Point point : segmentList) {
            // 找出每个段的震动数值
            List<Integer> opData = dataList.subList(point.getStart(), point.getEnd());
            opDataList.add(opData);
            // 求每个段的平均值
            Long total = opData.stream().mapToLong(Integer::intValue).sum();
            averageList.add(total / opData.size());
        }

        Integer minIndex = 0;
        Long minAverageValue = averageList.get(0);
        for (int m = 1; m < averageList.size(); m++) {
            if (averageList.get(m) < minAverageValue) {
                minAverageValue = averageList.get(m);
                minIndex = m;
            }
        }

        // 根据平均值最小的段，求开工阈值：K = S + ∆S
        List<Integer> opdata = opDataList.get(minIndex);
        Integer opMaxValue = opdata.get(0);
        Integer opMinValue = opdata.get(0);
        for (Integer data : opdata) {
            if (data > opMaxValue) {
                opMaxValue = data;
            }
            if (data < opMinValue) {
                opMinValue = data;
            }
        }

        System.out.println("minIndex = " + minIndex + " opData =" + opdata);
        //Integer K = opMaxValue + (opMaxValue - opMinValue);
        Integer K = minAverageValue.intValue() + (opMaxValue - opMinValue);
        System.out.println("minAverageValue=" + minAverageValue + " opMaxValue=" + opMaxValue);
        return K;
    }

    /**
     * 求开工阈值：K = S + ∆S
     */
    public static Integer getWorkThreshold(List<Integer> dataList, BigDecimal stop, Integer opNum) {
        // 1.求出设备采集数据中最大波动值：max - min
        Integer maxMinDiff = getMaxMinDiff(dataList);

        // 2.获取数组差分占比列表
        List<BigDecimal> list = getDiffPartList(dataList, maxMinDiff);

        // 3.找出稳定的段
        List<Point> segmentList = getStableSegmentList(list, stop, opNum);
        if (segmentList.size() < 1) {
            throw new RuntimeException("can not find stable segment data list");
        }

        System.out.println(segmentList);
        // 4.根据平均值最小的稳定段作为静止段，求出开工阈值
        Integer K = getOpThreshold(segmentList, dataList);
        return K;
    }

    /**
     * 开工判断条件
     * A.采集数据中有超过1/4点大于阈值判断为开工。
     * B.两次，连续两个点超过开工阈值判断为开工。
     * C.两次以上波动大于∆M认定为开工。
     */
    public static Boolean judgeWorkStatus(List<Integer> dataList, Integer workThreshold, Integer maxMinDiff) {
        if (dataList.size() < 1) {
            return Boolean.FALSE;
        }
        int num = 0;
        for (int i = 0; i < dataList.size(); i++) {
            Integer currentValue = dataList.get(i);
            if (currentValue > workThreshold) {
                if (i + 1 < dataList.size() && dataList.get(i + 1) > workThreshold) {
                    return Boolean.TRUE;
                }
                num++;
            }
        }
        if (num > dataList.size() / 4) {
            return Boolean.TRUE;
        }
        List<Integer> diffArray = getDiffArray(dataList);
        if (diffArray.size() < 2) {
            return Boolean.FALSE;
        }
        int diffNum = 0;
        for (int j = 0; j < diffArray.size(); j++) {
            Integer current = diffArray.get(j);
            if (current > maxMinDiff) {
                diffNum++;
            }
            if (diffNum > 2) {
                return Boolean.TRUE;
            }
        }

        return Boolean.FALSE;
    }

    public static void main(String[] args) throws Exception {
        // 1. 读取数据
        List<Integer> dataList = new ArrayList<>();

        // 波动阈值
        BigDecimal stop = new BigDecimal("0.1");
        // 开工采样时长
        Integer opNum = 18;

        // 2.求波动区间差值
        Integer maxMinDiff = OpModelUtil.getMaxMinDiff(dataList);

        // 3.求开工阈值
        Integer K = getWorkThreshold(dataList, stop, opNum);
        System.out.println("开工阈值：" + K);

        // 4.判断是否开工
        judgeWorkStatus(dataList, K, maxMinDiff);
    }

}
